#pragma once

#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "err.hpp"
#include "Thread.hpp"
#include "GardLock.hpp"
#include "ringqueue.hpp"

namespace ns_server
{
    // 绑定anyIP的好处
    // recvfrom 读取数据 buf 用户级缓冲区
    // 方便添加 /0
    // sendto
    // 处理文件时，都要注意 /0 的问题
    using func_t = std::function<std::string(std::string)>;

    const static uint16_t default_port = 8080;
    class udp_server
    {
    public:
        udp_server(uint16_t port = default_port)
            : _port(port)
        {
            pthread_mutex_init(&_lock, nullptr);

            _produce = new thread(1, std::bind(&udp_server::Recv, this));
            _consumer = new thread(2, std::bind(&udp_server::BroadCast, this));
        }

        void Start()
        {
            // 1.创建socket接口，打开网络文件
            _sock = socket(AF_INET, SOCK_DGRAM, 0); // 最后一个参数默认填0，在函数内部会自动匹配
            if (_sock < 0)
            {
                std::cerr << "create socket error: " << strerror(errno) << std::endl;
                exit(SOCK_ERR);
            }
            std::cout << "create socket: " << _sock << std::endl;

            sockaddr_in local;
            bzero(&local, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            // local.sin_addr.s_addr = inet_addr(_ip.c_str());
            // 因为云服务器的原因，不允许随意绑定IP
            local.sin_addr.s_addr = INADDR_ANY; // 绑定云服务器的任意IP
            // 2.给服务器指明IP和port
            int n = bind(_sock, (sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                std::cerr << "bind error: " << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            std::cout << "bind socket success: " << _sock << std::endl;
            
            _produce->run(); // 生产者：接收消息，并添加首次输入的用户
            _consumer->run();// 消费者: 持续地广播消息给所有用户
        }

        void AddUser(const sockaddr_in& user)
        {
            GardLock gard(&_lock);
            std::string port = std::to_string(user.sin_port);
            chart[port] = user;
        }

        void Recv() // 接收数据
        {
            char buffer[1024];
            while(true)
            {
                sockaddr_in peer;
                socklen_t len = sizeof(peer);
                recvfrom(_sock, buffer, sizeof(buffer)-1, 0, (sockaddr*)&peer, &len);
                
                std::string IP = inet_ntoa(peer.sin_addr);

                std::string message = IP + "> " + buffer;

                AddUser(peer); // 添加用户
                rq.push(message); // 推送消息

                bzero(buffer, sizeof(buffer)); // 清空缓冲区
            }
        }

        void BroadCast() // 发送数据
        {
            while(true)
            {
                std::string message;
                rq.pop(&message);
                
                // 将用户数据拷贝到vector中，就防止在广播数据的时候，不会因为锁的原因，无法插入，降低效率
                std::vector<sockaddr_in> copy_chart;
                {
                    GardLock gard(&_lock);
                    for(auto n: chart)
                        copy_chart.push_back(n.second);
                }

                for(auto n: copy_chart)
                    sendto(_sock, message.c_str(), message.size(), 0, (sockaddr*)&n, sizeof(n));
                
                std::cout << "Send success to " << copy_chart.size() << " people" << std::endl;
            }
        }

        // void Start()
        // {
        //     char buffer[1024];
        //     while (true)
        //     {
        //         sockaddr_in perr;
        //         socklen_t len = sizeof(perr);
        //         // 接收数据
        //         int n = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&perr, &len); // flags 确定IO方式选择，默认为0
        //         if (n > 0)
        //             buffer[n] = '\0';
        //         else
        //             continue;
        //
        //         std::string result = _func(std::string(buffer));
        //         // 发送数据
        //         sendto(_sock, result.c_str(), result.size(), 0, (sockaddr *)&perr, len);
        //     }
        // }

        ~udp_server()
        {
            pthread_mutex_destroy(&_lock);

            _produce->join();
            _consumer->join();

            delete _produce;
            delete _consumer;
        }

    private:
        int _sock;
        uint16_t _port;
        pthread_mutex_t _lock;
        ringqueue<std::string> rq;
        std::unordered_map<std::string, sockaddr_in> chart;

        thread* _produce;
        thread* _consumer;
    };
}